package com.winai.launcher.common;

import android.app.Activity;

import java.util.ArrayList;
import java.util.List;

/**
 * @Auth 强强
 * @Date 2019/8/28 18:06
 * @Desc 信号槽
 */
public class SignalSlot {
    //如果slot是匿名内部类，它会持有外部类的生命周期，而signaler会持有slot
    //即：如果signaler是常驻类对象，则slot不能是非常驻类的匿名对象，否则可能造成内存泄漏
    //如果slot在非常驻类里，应该申明为命名对象，然后在slot外部类生命周期将结束时，调用signaler的remove方法
    public static final int CARE_ANY = -1;//关注所有的通知

    static public abstract class Slot {
        private int mCare = CARE_ANY;//any case
        private Activity mActivite = null;

        private boolean isCare(int what) {
            return mCare == CARE_ANY || mCare == what;
        }

        //////////////////////////////////////
        public abstract void onSignal(int what, int arg1, int agr2, Object argObj);
    }

    public static class Signaler {
        private int mCurNotify = 0;
        private final static int LOCK_LIST = 1, LOCK_NOTIFY = 2;
        private MulitLock mMulitLock = new MulitLock();
        private List<Slot> mListSolt;

        public boolean addSolt(Slot slot) {
            return addSolt(CARE_ANY, slot);
        }

        public boolean addSolt(int care, Slot slot) {
            return addSolt(care, null, slot);
        }

        public boolean addSolt(Activity activity, Slot slot) {
            return addSolt(CARE_ANY, activity, slot);
        }

        public boolean addSolt(int care, Activity activity, Slot slot) {
            if (slot != null) {
                slot.mCare = care;
                slot.mActivite = activity;
                return addFinal(slot);
            }
            return false;
        }

        private boolean addFinal(Slot slot) {
            mMulitLock.lock(LOCK_LIST);
            if (mListSolt == null) {
                mListSolt = new ArrayList<>();
            }
            if (!isExist(slot)) {
                if (slot.mActivite != null) {
                    mListSolt.add(0, slot);//最后加入的ui-slot有最优通知权
                    mCurNotify++;//插入了一个在头部，如果正在通知，防止通知两次
                } else
                    mListSolt.add(slot);
            }
            mMulitLock.unlock(LOCK_LIST);
            return true;
        }

        /**
          * @Desc 移除Slot
          */
        public void removeSlot(Activity act) {
            if (mListSolt == null) {
                return;
            }
            if (act != null) {
                mMulitLock.lock(LOCK_LIST);
                for (int i = 0; i < mListSolt.size(); i++) {
                    if (mListSolt.get(i).mActivite == act) {
                        mListSolt.remove(i);
                        if (i < mCurNotify)//删除了一个，如果正在通知，不要漏掉
                            mCurNotify--;
                        break;
                    }
                }
                mMulitLock.unlock(LOCK_LIST);
            }
        }

        /////////////////////////////////////////////

        /**
          * @Desc 通知处理
          */
        public void signal(int what) {
            signal(what, 0, 0, null);
        }
        public void signal(int what, int arg1, int arg2, Object argObj) {
            Slot slot;
            mMulitLock.lock(LOCK_NOTIFY);//锁住通知，同时只能有一个线程进行通知
            mCurNotify = 0;//重置通知
            try {
                while ((slot = getForNotify()) != null) {//可以在正在进行的通知里，增加或者删除slot，不会有问题
                    if (slot.isCare(what)) {
                        slot.onSignal(what, arg1, arg2, argObj);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                mMulitLock.unlock(LOCK_NOTIFY);
            }
        }

        //获取一个用以通知
        private Slot getForNotify() {
            Slot sN = null;
            if (mListSolt != null) {
                mMulitLock.lock(LOCK_LIST);
                if (mCurNotify < mListSolt.size()) {
                    sN = mListSolt.get(mCurNotify++);
                }
                mMulitLock.unlock(LOCK_LIST);
            }
            return sN;
        }

        private boolean isExist(Slot sTarget) {
            return sTarget == null || mListSolt.indexOf(sTarget) != -1;
        }
    }

}
